
/**
\file
Definition of the rainbow::graphics_device class.
\author Sean Tromans
*/

#ifndef	RAINBOW_GRAPHICS_DEVICE_HPP
#define	RAINBOW_GRAPHICS_DEVICE_HPP

#include <rainbow/graphics_device/blend_state/blend_state.hpp>
#include <rainbow/graphics_device/depth_state/depth_state.hpp>
#include <rainbow/graphics_device/stencil_state/stencil_state.hpp>
#include <rainbow/graphics_device/culling_state/culling_state.hpp>
#include <rainbow/exception/exception.hpp>

#include <sun/lua/lua_fwd.hpp>
#include <needs_more_ninja/template/create_string.hpp>
#include <boost/unordered_map.hpp>
#include <stdexcept>
#include <cstdint>

namespace rainbow
{

	namespace detail
	{
		/** The integer type to be used by all state handles. */
		typedef std::uint32_t handle_t;
	}

	/** Exception thrown if the user requested a state which does not exist in the current device. */
	class state_not_found : public exception
	{
	public:
		
		state_not_found( std::string const & function_name, std::size_t handle_id )
			:	exception( nmn::create_string( "rainbow::state_not_found: In function %1% with state handle %2%", function_name, handle_id ) )
		{}
	};

	/**
	Manages the current state of the graphics device.
	\author Sean Tromans
	*/
	class graphics_device
	{
		//
		//	Public Member Types
		//
		public:

			/** A handle to a particular blend mode created on a graphics_device. */
			typedef detail::handle_t	blend_handle;

			/** A handle to a particular depth state created on a graphics_device. */
			typedef detail::handle_t	depth_handle;

			/** A handle to a particular stencil state created on a graphics_device. */
			typedef detail::handle_t	stencil_handle;

			/** A handle to a particular culling mode created on a graphics_device. */
			typedef detail::handle_t	culling_handle;

		//
		//	Public Member Function
		//
		public:

			/**
			Default Constructor
			\author Sean Tromans
			*/
			graphics_device( );

			/**
			Adds a new blend_state to the graphics_device.
			@param new_state The state to register with the device.
			@return A handle to a new blend_state unless there is already a blend_state matching new_state, in which case a handle to the old state will be returned.
			\author Sean Tromans
			*/
			blend_handle register_state( blend_state const & new_state );

			/**
			Gets a reference to a blend_state from the graphics_device.
			@param handle The blend_handle to the blend_state required.
			@return A refernce to a blend_state.
			\throws state_not_found
			\author Sean Tromans
			*/
			blend_state & get_blend_state( blend_handle const & handle );

			/**
			Sets the currently active blend_state.
			@param handle The handle to the blend_state to activate.
			\throws state_not_found
			\author Sean Tromans
			*/
			void activate_blend_state( blend_handle const & handle );

			/**
			Returns the handle to the currently active blend_state.
			@return The handle to the current blend_state.
			\author Sean Tromans
			*/
			blend_handle active_blend_state( ) const
			{
				return m_current_blend_state;
			}

			/**
			Adds a new depth_state to the graphics_device.
			@param new_state The state to register with the device.
			@return A handle to a new depth_state unless there is already a depth_state matching new_state, in which case a handle to the old state will be returned.
			\author Sean Tromans
			*/
			depth_handle register_state( depth_state const & new_state );

			/**
			Gets a reference to a depth_state from the graphics_device.
			@param handle The depth_handle to the depth_state required.
			@return A refernce to a depth_state.
			\throws state_not_found
			\author Sean Tromans
			*/
			depth_state & get_depth_state( depth_handle const & handle );

			/**
			Sets the currently active depth_state.
			@param handle The handle to the depth_state to activate.
			\throws state_not_found
			\author Sean Tromans
			*/
			void activate_depth_state( depth_handle const & handle );

			/**
			Returns the handle to the currently active depth_state.
			@return The handle to the current depth_state.
			\author Sean Tromans
			*/
			depth_handle active_depth_state( ) const
			{
				return m_current_depth_state;
			}

			/**
			Adds a new stencil_state to the graphics_device.
			@param new_state The state to register with the device.
			@return A handle to a new stencil_state unless there is already a stencil_state matching new_state, in which case a handle to the old state will be returned.
			\author Sean Tromans
			*/
			stencil_handle register_state( stencil_state const & new_state );

			/**
			Gets a reference to a stencil_state from the graphics_device.
			@param handle The stencil_handle to the stencil_state required.
			@return A refernce to a stencil_state.
			\throws state_not_found
			\author Sean Tromans
			*/
			stencil_state & get_stencil_state( stencil_handle const & handle );

			/**
			Sets the currently active stencil_state.
			@param handle The handle to the stencil_state to activate.
			\throws state_not_found
			\author Sean Tromans
			*/
			void activate_stencil_state( stencil_handle const & handle );

			/**
			Returns the handle to the currently active stencil_state.
			@return The handle to the current stencil_state.
			\author Sean Tromans
			*/
			stencil_handle active_stencil_state( ) const
			{
				return m_current_stencil_state;
			}

			/**
			Adds a new culling_state to the graphics_device.
			@param new_state The state to register with the device.
			@return A handle to a new culling_state unless there is already a culling_state matching new_state, in which case a handle to the old state will be returned.
			\author Sean Tromans
			*/
			culling_handle register_state( culling_state const & new_state );

			/**
			Gets a reference to a culling_state from the graphics_device.
			@param handle The culling_handle to the culling_state required.
			@return A refernce to a culling_state.
			\throws state_not_found
			\author Sean Tromans
			*/
			culling_state & get_culling_state( culling_handle const & handle );

			/**
			Sets the currently active culling_state.
			@param handle The handle to the culling_state to activate.
			\throws state_not_found
			\author Sean Tromans
			*/
			void activate_culling_state( culling_handle const & handle );

			/**
			Returns the handle to the currently active culling_state.
			@return The handle to the current culling_state.
			\author Sean Tromans
			*/
			culling_handle active_culling_state( ) const
			{
				return m_current_culling_state;
			}

		//
		//	Private Member Types
		//
		private:

			/** Collection of blend_states indexed by unique ID. */
			typedef boost::unordered_map< blend_handle, blend_state >	blend_state_map_t;

			/** Collection of depth_states indexed by unique ID. */
			typedef boost::unordered_map< depth_handle, depth_state >	depth_state_map_t;

			/** Collection of stencil_states indexed by unique ID. */
			typedef boost::unordered_map< stencil_handle, stencil_state >	stencil_state_map_t;

			/** Collection of culling_states indexed by unique ID. */
			typedef boost::unordered_map< culling_handle, culling_state >	culling_state_map_t;

		//
		//	Private Member Variables
		//
		private:

			blend_state_map_t	m_blend_states;
			depth_state_map_t	m_depth_states;
			stencil_state_map_t	m_stencil_states;
			culling_state_map_t	m_culling_states;
			detail::handle_t	m_current_id;	///< The last assigned unique ID for any state.

			blend_handle		m_current_blend_state;
			depth_handle		m_current_depth_state;
			stencil_handle		m_current_stencil_state;
			culling_handle		m_current_culling_state;

		//
		//	Static Member Functions
		//
		public:

			/**
			Exposes the graphics_device to lua.
			@param L The Lua State being initialised.
			\author Sean Tromans.
			*/
			static void lua_interface( lua_State * L );
	};

}

#endif	//RAINBOW_GRAPHICS_DEVICE_HPP
